package com.holly.unit.bpmn.designer.core.adapter;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.holly.unit.bpmn.designer.constants.BpmnDesignerConstants;
import com.holly.unit.bpmn.designer.core.analysis.SpNodeAnalysis;
import com.holly.unit.bpmn.designer.core.analysis.TjNodeAnalysis;
import com.holly.unit.bpmn.designer.enums.NodeTypeEnum;
import com.holly.unit.bpmn.designer.model.*;
import org.activiti.bpmn.model.ExclusiveGateway;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.UserTask;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 类描述: 把JSON结构的数据转化成流程文档XML
 *
 * @author fxr
 * @version 1.0.0
 * @date 2022/5/12 14:03
 */
public class BpmnDesignerAdapter {


    /***
     * 把流程设计器领域模型转化成流程Bpmn规范文档
     * @param model
     * @return
     */
    public static String bpmn(BpmnDesignerModel model,BpmnDesignerXml xml){

        ProcessInfo processInfo = model.getProcessInfo();
        // 解析JSON
        List<ChildNode> allChildNodes = new ArrayList<>();
        List<ConditionNode> conditionNodes = new ArrayList<>();

        if (Objects.nonNull(processInfo.getChildNode())) {
            // 解析所有的子节点
            fetchChildNode("",processInfo.getChildNode(),allChildNodes,conditionNodes);
        }

        if (Objects.nonNull(processInfo.getConditionNodes())) {
            // 解析所有的条件节点
            fetchConditionNode(processInfo.getConditionNodes(),allChildNodes,conditionNodes);
        }

        List<ChildNode> approverNodes = allChildNodes.stream()
                .filter(e -> e.getType().equals(NodeTypeEnum.SP.fieldName()))
                .collect(Collectors.toList());

        // 创建用户任务
        List<UserTask> userTasks = SpNodeAnalysis.getInstance().createUserTask(approverNodes);
        //创建排他网关
        List<ExclusiveGateway> gateways = TjNodeAnalysis.getInstance().createGateWay(conditionNodes);
        // 创建所有连线
        List<SequenceFlow> sequenceFlows = new ArrayList<>();

        createUserTaskArrow(processInfo,approverNodes,allChildNodes,conditionNodes,sequenceFlows);
        createConditionArrow(processInfo,allChildNodes,conditionNodes,sequenceFlows);

        // 创建start节点
        String startNextId = nextNodeId(processInfo,processInfo,allChildNodes,conditionNodes);
        SequenceFlow startSequenceFlow = new SequenceFlow();
        startSequenceFlow.setSourceRef(processInfo.getNodeId());
        startSequenceFlow.setTargetRef(startNextId);
        sequenceFlows.add(startSequenceFlow);

        String rst = "";
        try {
            rst = xml.builder().model(model).task(userTasks).line(sequenceFlows).gateway(gateways).generate();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return rst;
    }

    /***
     * 查询指定节点
     * @param json
     * @param nodeId
     * @return
     */
    public static ChildNode fetchChildNodeByNodeId(String json,String nodeId) {
        BpmnDesignerModel model = JSON.parseObject(json,BpmnDesignerModel.class);
        return fetchChildNodeByNodeId(model,nodeId);
    }

    /***
     * 查询指定节点
     * @param model
     * @param nodeId
     * @return
     */
    public static ChildNode fetchChildNodeByNodeId(BpmnDesignerModel model,String nodeId) {
        ProcessInfo processInfo = model.getProcessInfo();
        List<ChildNode> allChildNodes = new ArrayList<>();
        List<ConditionNode> conditionNodes = new ArrayList<>();
        fetchChildNode("",processInfo.getChildNode(),allChildNodes,conditionNodes);
        for (ChildNode node:allChildNodes) {
            if (node.getNodeId().equals(nodeId)) return node;
        }
        return null;
    }

    private static void fetchChildNode(String outerNodeId,ChildNode childNode,List<ChildNode> allChildNodes,List<ConditionNode> conditionNodes) {
        if (childNode.getType().equals(NodeTypeEnum.CS.fieldName())) {// 抄送节点

        }
        allChildNodes.add(childNode);
        childNode.setOuterNodeId(outerNodeId);

        // 如果下一个节点不空，递归处理
        if (childNode.getChildNode() != null) {
            fetchChildNode(outerNodeId,childNode.getChildNode(),allChildNodes,conditionNodes);
        }

        if (childNode.getConditionNodes() != null && childNode.getConditionNodes().size()>0) {
            // 获取条件节点
            fetchConditionNode(childNode.getConditionNodes(),allChildNodes,conditionNodes);
        }
    }

    private static void fetchConditionNode(List<ConditionNode> conditionNodes,List<ChildNode> allChildNodes,List<ConditionNode> conditions) {
        for (ConditionNode conditionNode:conditionNodes) {
            conditions.add(conditionNode);
            if (conditionNode.getChildNode() != null) {
                fetchChildNode(conditionNode.getPrevId(),conditionNode.getChildNode(),allChildNodes,conditions);
            }
            if (conditionNode.getConditionNodes() != null) {
                fetchConditionNode(conditionNode.getConditionNodes(),allChildNodes,conditions);
            }
        }
    }

    private static void createUserTaskArrow(ProcessInfo processInfo,List<ChildNode> approverNodes,
                                           List<ChildNode> allChildNodes,List<ConditionNode> conditionNodes,
                                           List<SequenceFlow> sequenceFlows) {
        for (ChildNode node:approverNodes) {
            SequenceFlow flow = new SequenceFlow();
            flow.setTargetRef(nextNodeId(node,processInfo,allChildNodes,conditionNodes));
            flow.setSourceRef(node.getNodeId());
            sequenceFlows.add(flow);
        }

    }

    private static void createConditionArrow(ProcessInfo processInfo, List<ChildNode> allChildNodes,
                                             List<ConditionNode> conditionNodes, List<SequenceFlow> sequenceFlows) {

        for (ConditionNode node:conditionNodes) {
            SequenceFlow flow = new SequenceFlow();
            flow.setSourceRef(node.getPrevId()+ BpmnDesignerConstants.GATEWAY_SUFFIX);
            flow.setTargetRef(nextNodeId(node,processInfo,allChildNodes,conditionNodes));
            flow.setName(node.getProperties().getConditionLebel());
            flow.setConditionExpression(node.getProperties().getCondition());
            sequenceFlows.add(flow);
        }


    }

    /***
     * 获取node下一个节点
     * @param currentNode 当前节点
     * @param processInfo
     * @param allChildNodes
     * @param conditionNodes
     * @return
     */
    private static String nextNodeId(Node currentNode, ProcessInfo processInfo,
                                     List<ChildNode> allChildNodes, List<ConditionNode> conditionNodes) {
        String nextId = "";
        // 如果当前节点配置了条件，按照条件走
        if (currentNode.getConditionNodes() != null && currentNode.getConditionNodes().size()>0) {
            nextId = currentNode.getNodeId()+BpmnDesignerConstants.GATEWAY_SUFFIX;
        } else if (currentNode.getChildNode() != null) {
            //当前节点没有配置条件，然而有子节点
            ChildNode nextNode = currentNode.getChildNode();
            if (nextNode.getType().equals(NodeTypeEnum.SP.fieldName())) {//如果是审批节点
                nextId = nextNode.getNodeId();
            } else if (nextNode.getType().equals(NodeTypeEnum.EMPTY.fieldName())) {
                // 如果下一个节点是空节点，说明父节点没有condition
                throw new IllegalArgumentException("数据有问题");
            } else if (nextNode.getType().equals(NodeTypeEnum.CS.fieldName())) {//如果是抄送人节点
                // TODO 抄送人节点处理
            } else {
                throw new RuntimeException("暂未开发");
            }
        } else {
            // 既没有子节点，也没有条件节点，去外层寻找
            String outerNodeId = "";
            if (currentNode instanceof ChildNode) {
                outerNodeId = ((ChildNode) currentNode).getOuterNodeId();
            } else if (currentNode instanceof ConditionNode) {
                outerNodeId = currentNode.getPrevId();
            }

            return fetchOuterNextNode(outerNodeId,processInfo,allChildNodes,conditionNodes);
        }
        return nextId;
    }

    /**
     * 获取外层的下一个节点
     * @param outerNodeId
     * @param processInfo
     * @param allChildNodes
     * @param conditionNodes
     * @return
     */
    private static String fetchOuterNextNode(String outerNodeId,ProcessInfo processInfo,
                                             List<ChildNode> allChildNodes, List<ConditionNode> conditionNodes){
        Map<String,ChildNode> childNodeMap = allChildNodes.stream()
                .collect(Collectors.toMap(ChildNode::getNodeId,ChildNode -> ChildNode));
        Map<String,ConditionNode> conditionNodeMap = conditionNodes.stream()
                .collect(Collectors.toMap(ConditionNode::getNodeId,ConditionNode->ConditionNode));
        // 递归结束条件
        if (StrUtil.isEmpty(outerNodeId)) {
            //当前节点是最外层节点，直接指向流程结束节点
            return BpmnDesignerConstants.END_EVENT;
        } else {
            ChildNode outerNode = childNodeMap.get(outerNodeId);
            ConditionNode condition = conditionNodeMap.get(outerNodeId);
            ChildNode outerChildNode = null;
            String outerOuterNodeId = "";
            if (Objects.nonNull(outerNode)) {
                outerChildNode = outerNode.getChildNode();
                outerOuterNodeId = outerNode.getOuterNodeId();
            } else if (Objects.nonNull(condition)) {
                outerChildNode = condition.getChildNode();
                outerOuterNodeId = condition.getPrevId();
            } else {
                // 如果不是条件节点，也不是子节点，就是开始节点StartNode
                if (Objects.nonNull(processInfo.getChildNode())) {
                    outerChildNode = processInfo.getChildNode();
                    outerOuterNodeId = "";
                }
            }

            if (Objects.nonNull(outerChildNode)) {
                if (outerChildNode.getType().equals(NodeTypeEnum.EMPTY.fieldName())) {//如果是空节点，就接着查找下一个节点
                    return nextNodeId(outerChildNode,processInfo,allChildNodes,conditionNodes);
                } else {
                    // 不是空节点，就返回节点id
                    return outerChildNode.getNodeId();
                }
            } else {
                // 递归到外层节点的外层
                return fetchOuterNextNode(outerOuterNodeId,processInfo,allChildNodes,conditionNodes);
            }
        }
    }
}
